﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace JOATServer.SocietyFramework.Core.XML
{
    /// <summary>
    /// Abstract automaton which is dynamically written. The code is made of different classes which represent instructions.<br/>
    /// It must be serialized/deserialized entirely to/from XML.<br/>
    /// Author: Micu Andrei
    /// </summary>
    public abstract partial class BaseXMLAutomaton : BaseAutomaton
    {
        protected Dictionary<string, object> variables;             //holds the variables in the automaton
        public string Name { get; set; }                            //the name of the automaton (used for serialization)
        public bool RegisterAtStart { get; set; }                   //if it is true, then the automaton is registered at start in the router

        public BaseXMLAutomaton()
        {
            variables = new Dictionary<string, object>();
            Name = null;
            RegisterAtStart = false;
        }

        public override void Start()
        {
            if (automatonThread != null && automatonThread.IsAlive)
            {
                this.StopSafely(() =>
                {
                    if (RegisterAtStart)
                    {
                        MessageRouter.Instance.Subscribe(this.Name, this);
                    }
                    automatonThread = new Thread(() =>
                    {
                        Run();
                        if (callback != null)
                        {
                            callback();		//execute callback method after stopping the while loop
                        }
                    });
                    automatonThread.Name = "AutomatonThread_" + this.GetType().Name;
                    automatonThread.Start();
                });
            }
            else
            {
                if (RegisterAtStart)
                {
                    MessageRouter.Instance.Subscribe(this.Name, this);
                }
                automatonThread = new Thread(() =>
                {
                    Run();
                    if (callback != null)
                    {
                        callback();		//execute callback method after stopping the while loop
                    }
                });
                automatonThread.Name = "AutomatonThread_" + this.GetType().Name;
                automatonThread.Start();
            }
        }

        protected object[] snapshot = new object[4];

        public void SaveSnapshot()
        {
            snapshot[0] = GetDeepCopy(currentStateName);
            snapshot[1] = GetDeepCopy(currentMessage);
            snapshot[2] = GetDeepCopy(messageList);
            snapshot[3] = GetDeepCopy(variables);
        }

        protected object GetDeepCopy(Object source)
        {
            if (source == null) return null;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(memoryStream, source);
                memoryStream.Position = 0;

                return formatter.Deserialize(memoryStream);
            }
        }

        public void RestoreSnapshot()
        {
            currentStateName = (String)snapshot[0];
            currentMessage = (Message)snapshot[1];
            messageList = (MessageList)snapshot[2];
            variables = (Dictionary<string, object>)snapshot[3];
        }
    }
}
